package com.zyk.scaffold.core.lock;

import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;

import java.util.Collections;

/**
 * 可重入分布式锁[直接返回，无等待队列]
 */
public class ReentrantRedisLock {

    private RedisTemplate redisTemplate;
    private DefaultRedisScript<Long> lockScript;
    private DefaultRedisScript<Object> unlockScript;
    private String instanceId;

    public ReentrantRedisLock(RedisTemplate redisTemplate, String instanceId) {
        this.redisTemplate = redisTemplate;
        this.instanceId = instanceId;
        this.lockScript = new DefaultRedisScript<>();
        this.lockScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("META-INF/scripts/lock/reentrant/lock.lua")));
        this.lockScript.setResultType(Long.class);
        this.unlockScript = new DefaultRedisScript<>();
        this.unlockScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("META-INF/scripts/lock/reentrant/unlock.lua")));
    }

    /**
     * 获取锁
     *
     * @param lockName    锁名称
     * @param releaseTime 超时时间(单位:秒)
     * @return key 解锁标识
     */
    public boolean tryLock(String lockName, long releaseTime) {
        // 执行脚本
        Long result = (Long) redisTemplate.execute(lockScript, Collections.singletonList(lockName), getThreadKey(), releaseTime);
        // 判断结果
        if (result != null && result.intValue() == 1) {
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }

    /**
     * 释放锁
     *
     * @param lockName 锁名称
     */
    public void unlock(String lockName) {
        // 执行脚本
        redisTemplate.execute(unlockScript, Collections.singletonList(lockName), getThreadKey(), null);
    }

    private String getThreadKey() {
        return instanceId + Thread.currentThread().getId();
    }
}
